package org.java.user.service;

import org.java.commons.vo.Result;
import org.java.user.api.damain.Role;
import org.java.user.api.damain.User;
import org.java.user.domain.DestroyedUser;
import org.java.user.repositiry.DestroyedUserRepository;
import org.java.user.repositiry.RoleRepository;
import org.java.user.repositiry.UserRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.util.*;

@Service
public class UserServiceImpl implements UserService, InitializingBean {
    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    //    @Autowired
//    private GatewayLogout gatewayLogout;
    @Autowired
    private DestroyedUserRepository destroyedUserRepository;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化的时候，首先调用构造器，然后调用各个set方法完成属性注入。
        // 最后如果实现了InitializingBean接口，就会调用afterPropertiesSet方法。

        //检查并初始化默认角色（role）的方法
        checkAndInitDefaultRoles();
    }

    private void checkAndInitDefaultRoles() {
        // 查询roleKey(标识身份)为ADMIN的角色，如果不存在则新增一条，并且builtIn（内置角色）为true、fixed（固定角色）为false。
        roleRepository.findByRoleKey("ADMIN").ifPresentOrElse((role) -> {
        }, () -> {
            Role role = new Role();
            role.setBuiltIn(true);
            role.setFixed(false);
            role.setRoleKey("ADMIN");
            role.setName("管理员");
            this.roleRepository.save(role);
        });
        // 查询roleKey为USER的角色，如果不存在则新增一条，并且builtIn和fixed都为true。
        roleRepository.findByRoleKey("USER").ifPresentOrElse((role) -> {
        }, () -> {
            Role role = new Role();
            role.setBuiltIn(true);
            role.setFixed(false);
            role.setRoleKey("USER");
            role.setName("用户");
            this.roleRepository.save(role);
        });
    }
//    注册
    @Override
    public Result registry(User user) {
        // 1.检查登录名是否被占用
        Optional<User> optionalUser = this.userRepository.findByLoginName(user.getLoginName());
        if (optionalUser.isPresent()) {
            // 已经存在
            return Result.error("登录名已经被占用");
        }
//        // 2.查询固定的内置角色，添加给User对象
//        List<Role> roles = this.roleRepository.findByBuiltInIsTrueAndFixedIsTrue();
////        user.setRoles(roles);
//
//        if (user.getRoles() == null) {
//            user.setRoles(new LinkedList<>());
//        }
//
////          此时需要合并角色，使用hashSet能保证唯一
//        Set<Role> allRoles = new HashSet<>();
////        为保证角色是唯一的，必须重写hashCode和equals方法
//        allRoles.addAll(roles);//固定角色
////        把新增页面传进来的角色也添加进来
//        allRoles.addAll(user.getRoles());//界面传进来的角色
////        把两个角色结合在一起
//        user.getRoles().clear();
//        user.getRoles().addAll(allRoles);
        this.roles(user);
        // 3.保存用户信息到数据库
        String password = user.getPassword();
//      如果不等于空才  加密
        if (!StringUtils.isEmpty(password)) {
            password = this.passwordEncoder.encode(password);
            user.setPassword(password);
        }

        this.userRepository.save(user);
        return Result.ok("注册成功");
    }

    @Override
    public User loadUserByName(String name) {
        //判断通过方式1、通过登录名2、通过手机3、通过邮箱
        //user 通过数据库.查询名字.其它（other为空）；  orElse如果this.value != null ? this.value : other这个值不等于空；就放回这个值，或者other
        System.out.println(name);
        User user = this.userRepository.findByLoginName(name).orElse(null);
        if (user == null) {
            user = this.userRepository.findByMobilePhone(user.getMobilePhone()).orElse(null);
        }
        if (user == null) {
            user = this.userRepository.findByEmail(user.getEmail()).orElse(null);
        }
        System.out.println(user);
        return user;
    }

    @Override
    public User findById(String id) {
        return this.userRepository.findById(id).orElse(null);
    }

    @Override
    @Transactional // 在事务范围内容，通过findById查询出来的对象调用set方法，会自动同步到数据库
    public Result save(User user) {

        if(StringUtils.isEmpty(user.getId())){
            return this.registry(user);
        }

        //（旧的用户信息）根据id把原来的用户信息查询出来，因为有些字段不需要修改
        User old = this.userRepository.findById(user.getId()).orElse(null);

        //名字使用旧的，因为它不能修改，但是需要显示
        old.setName(user.getName());
        //手机号码不能为空，而且新的手机号码不等于旧的手机号码
        if (user.getMobilePhone() != null && !user.getMobilePhone().equals(old.getMobilePhone())) {
            //新旧手机号码不一样，需要检查是否有其他人使用
            User test = this.userRepository.findByMobilePhone(user.getMobilePhone()).orElse(null);
            //查询到这个手机被占用，并且用户新的id不等于旧的id
            if (test != null && !test.getId().equals(user.getId())) {
                return Result.error("手机号已被其他人占用");
            }
        }
        //邮箱不能为空，而且新的邮箱和旧的邮箱不相同
        if (user.getEmail() != null && !user.getEmail().equals(old.getEmail())) {
            //新旧邮箱不一样，检查是否有其他人使用
            User test = this.userRepository.findByEmail(user.getEmail()).orElse(null);
            //查询到这个邮箱被占用，并且用户新的id不等于旧的id
            if (test != null && !test.getId().equals(user.getId())) {
                return Result.error("邮箱已被其他人使用");
            }
        }

        old.setEmail(user.getEmail());
        old.setMobilePhone(user.getMobilePhone());
        if (old.getAccountExpireTime() != null) {
            // 如果有账号过期时间，那么直接把过期时间设置为半年后。
            // 当然也可以做一个【充值续期】的功能。
            long time = System.currentTimeMillis() + (1000 * 60 * 60 * 24 * 180);
            old.setAccountExpireTime(new Date(time));
        }

//        原来有传user进来所以需要先把旧的覆盖
        old.setRoles(user.getRoles());
        this.roles(old);

        return Result.ok("修改成功");
    }
//  处理角色的关联
    private void roles(User user){
        // 2.查询固定的内置角色，添加给User对象
        List<Role> fixedRoles = this.roleRepository.findByFixedIsTrue();
//        user.setRoles(roles);

        if (user.getRoles() == null) {
            user.setRoles(new LinkedList<>());
        }

//          此时需要合并角色，使用hashSet能保证唯一
        Set<Role> allRoles = new HashSet<>();
//        为保证角色是唯一的，必须重写hashCode和equals方法
        allRoles.addAll(fixedRoles);//固定角色
//        把新增页面传进来的角色也添加进来
        allRoles.addAll(user.getRoles());//界面传进来的角色
//        把两个角色结合在一起
        user.getRoles().clear();
        user.getRoles().addAll(allRoles);
    }



    @Override
    @Transactional
    public Result changePassword(String id, String oldPassword, String newPassword, String confirmPassword) {
        // 1.判断两次输入的密码是否一致
        //如果为空，或者密码不相同
        if (StringUtils.isEmpty(newPassword) || !newPassword.equals(confirmPassword)) {
            return Result.error("密码为空或者两次密码不相同");
        }
        // 2.根据id查询User对象
        User user = this.userRepository.findById(id).orElse(null);
        // 3.对oldPassword进行加密，判断加密后的密文是否和数据库里面的密码匹配
        //旧的密码跟数据库的密码不匹配
        if (!this.passwordEncoder.matches(oldPassword, user.getPassword())) {
            return Result.error("旧密码输入错误");
        }
        // 4.修改User对象的密码为newPassword，并且要加密
        String password = this.passwordEncoder.encode(newPassword);
        user.setPassword(password);

        return Result.ok("密码修改成功");
    }

    @Override
    @Transactional
    public Result destroy(String id, String password) {
        User user = this.userRepository.findById(id).orElse(null);
        if (!this.passwordEncoder.matches(password, user.getPassword())) {
            return Result.error("密码输入错误");
        }
        // 账户已经停止使用
        user.setEnabled(false);
        // 账户被锁定使用，给管理员封号使用的
//        user.setAccountNonLocked(false);
        // 在销户数据表中，记录完整的销户数据
        DestroyedUser du = new DestroyedUser();
        BeanUtils.copyProperties(user, du, "roles");
        du.setId(null);
        du.setUserId(user.getId());
        this.destroyedUserRepository.save(du);

        // 把登录名空出来，给其他用户使用
        user.setLoginName(null);
        user.setEmail(null);
        user.setMobilePhone(null);

        return Result.ok("注销成功");
    }

    @Override
    public List<Role> findAllRoles() {
        Sort sort = Sort.by(Sort.Order.desc("builtIn"), Sort.Order.asc("roleKey"));
        return roleRepository.findAll(sort);
    }

    @Override
    @Transactional
    public Result save(Role role) {
        //1、检查name和key的规则是否满足条件
        if (StringUtils.isEmpty(role.getName()) || role.getName().length() < 3) {
            return Result.error("角色名称为空或者长度小于3");
        }
        if (StringUtils.isEmpty(role.getRoleKey()) || role.getRoleKey().length() < 3 || !role.getRoleKey().matches("^[0-9]+$")) {
            return Result.error("角色的KEY为空或者长度小于3，也可能是包含了非法字符（只能包含0-9的数字和下划线）");
        }
        //2、检查key是否重复
        //获取到数据库中所有key
        Role old = this.roleRepository.findByRoleKey(role.getRoleKey()).orElse(null);
        //如果数据库中的key不是空的，并且这两个ID不能相同，因为有些人犯贱用同一个id更改相同的信息，数据库原来就有这条信息了，所以要判断一下
        if (old != null && !old.getId().equals(role.getId())) {
            //说明被占用了
            return Result.error("角色的key已经被使用");
        }
        //3、保存数据
        this.roleRepository.save(role);

        return Result.ok("角色更新成功！");
    }

    @Override
    public Result deleteRole(String id) {
        //1、判断用户是否有使用此角色，如果有正在使用则不能删除
        List<Role> roles = new LinkedList<>();
        Role role = this.roleRepository.findById(id).orElse(null);
        if (role.isBuiltIn()) {
            return Result.error("这是一个内置角色，不能删除");
        }
        roles.add(role);
        //所有集合的查询都加In在结尾
        List<User> users = this.userRepository.findByRolesIn(roles);
        //2、执行删除
        if (!users.isEmpty()) {
            return Result.error("角色正在使用中，不能删除");
        }
        this.roleRepository.delete(role);
        return Result.ok("角色删除成功!");
    }

    @Override
    public Page<User> searchUser(String keyword, int pageNumber, int pageSize, String orderBy, String direction) {

        Sort sort;
        if (!StringUtils.isEmpty(orderBy) && !StringUtils.isEmpty(direction)) {
            if ("ASC".equalsIgnoreCase(direction)) {
                // 升序、顺序，从小到大
                sort = Sort.by(Sort.Order.asc(orderBy));
            } else {
                // 从大到小，降序、倒序
                sort = Sort.by(Sort.Order.desc(orderBy));
            }
        } else {
            sort = Sort.unsorted();
        }

        // 构建分页条件
        Pageable pageable = PageRequest.of(pageNumber, pageSize, sort);
        Page<User> page;
        if (!StringUtils.isEmpty(keyword)) {
            // 根据关键字搜索数据，可能没有输入关键字，需要判断是否有关键字才进行关键字搜索
            // 由于条件可能比较复杂，所以建议使用JPA的自定义查询
            Specification<User> spec = (root, query, criteriaBuilder) -> {
                // root 表示查询对象的根，基本上表示从哪个类里面查询数据
                // query是一个动态条件对象
                // criteriaBuilder用于构建动态条件

                // 最后把条件构建好以后，返回Predicate对象（谓词）

                // 如果有keyword，那么就把keyword作为一个or条件，根据登录名、姓名查询数据
                Predicate namePredicate = criteriaBuilder.like(root.get("name"), "%" + keyword + "%");
                Predicate loginNamePredicate = criteriaBuilder.like(root.get("loginName"), keyword + "%");
                // 构建or查询
                // where name like ? or loginName like ?
                return criteriaBuilder.or(namePredicate, loginNamePredicate);
            };
            page = this.userRepository.findAll(spec, pageable);
        } else {
            page = this.userRepository.findAll(pageable);
        }

        return page;
    }
}
